#include "master.h"
#include "slave_protocol.h"
#include "slave.h"
#include "master_internal_cmd.h"
#include "../base/th.h"


#include "../base/kvcfg.h"
#include "../base/mq.h"
#include "../base/list.h"
#include "../base/map.h"
#include "../base/th.h"
#include "../base/dl.h"
#include "../base/systime.h"

#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <malloc.h>


#define MASTER_SYS_LOG_BUFFER_SIZE 1024

C_CODE_BEGIN



struct master_cfg_file {
	struct kv_element* pars;
	/* default initialization order sequence 
	 * This cmds area will hold all the initialization
	 * command for the master component in its execution
	 * sequence.
	 */
	struct list* cmds;
};

struct master_sys_log {
	// we will do some trick that makes us can 
	FILE* l[4]; 
};

struct _master_sys_log_display_option {
	int idx;
	// at most 3 file descriptor will be assigned stderr stdout
	FILE* err,*out;

	bool flush;

	void (*op)();
};


static void _master_sys_log_abort_on_fatal(void) {
#if defined(_DEBUG) && defined(_MSC_VER)
	__asm int 3
#else
	abort();
#endif 
}

static struct _master_sys_log_display_option 
	_master_sys_log_static_init( int val , FILE* err, FILE* out , bool f , void (*op)() ) {
	struct _master_sys_log_display_option ret ={
		val,err,out,f,op
	};
	return ret;
}

static struct _master_sys_log_display_option _master_sys_log_info_option;

static struct _master_sys_log_display_option _master_sys_log_warn_option;

static struct _master_sys_log_display_option _master_sys_log_error_option;

static struct _master_sys_log_display_option _master_sys_log_fatal_option;

#define MASTER_SYS_LOG_LEVEL_NUM 4


#ifdef _WIN32
static const char* _log_file_name[MASTER_SYS_LOG_LEVEL_NUM] = {
	"master.sys.info.log.txt",
	"master.sys.warn.log.txt",
	"master.sys.error.log.txt",
	"master.sys.fatal.log.txt"
};
#else
/*
 * On Linux, the log file will goto /var/logs folder. This is the 
 * convention that Linux software has .
 */
static const char* _log_file_name[MASTER_SYS_LOG_LEVEL_NUM] = {
	"/var/logs/master.sys.info.log.txt",
	"/var/logs/master.sys.warn.log.txt",
	"/var/logs/master.sys.error.log.txt",
	"/var/logs/master.sys.fatal.log.txt"
};
#endif// _WIN32


#ifdef _WIN32 
#define _strcasecmp _stricmp
#else
#define _strcasecmp strcasecmp
#endif // __WIN32


static bool _master_sys_log_init( struct master_sys_log* l ) {
	int i = 0 ;
	_master_sys_log_info_option = _master_sys_log_static_init(0,NULL,stdout,false,NULL);
	_master_sys_log_warn_option = _master_sys_log_static_init(1,NULL,stdout,true,NULL);
	_master_sys_log_error_option = _master_sys_log_static_init(2,stderr,stdout,true,NULL);
	_master_sys_log_error_option = _master_sys_log_static_init(2,stderr,stdout,true,NULL);
	_master_sys_log_fatal_option 
		= _master_sys_log_static_init(2,stderr,stdout,true,_master_sys_log_abort_on_fatal);
	for( ; i < MASTER_SYS_LOG_LEVEL_NUM ; ++i ) {
		(l->l)[i] = fopen(_log_file_name[i],"w+");
		if( (l->l)[i] == NULL)
			return false;
	}
	return true;
}

static void _master_sys_log_shutdown( struct master_sys_log* l ) {
	int i = 0 ;
	for( ; i < MASTER_SYS_LOG_LEVEL_NUM ; ++i ) {
		if( l->l[i] )
			fclose(l->l[i]);
	}
}

static const struct _master_sys_log_display_option* _master_sys_log_translate_display_option( const char* op ) {
	if( _strcasecmp(op,"warn") == 0 ) {
		return &_master_sys_log_warn_option;
	} else if( _strcasecmp(op,"error")==0 ) {
		return &_master_sys_log_error_option;
	} else if( _strcasecmp(op,"fatal")==0 ) {
		return &_master_sys_log_fatal_option;
	} else{
		return &_master_sys_log_info_option;
	}
}

static void _master_sys_log_do_display( struct master* m , const struct _master_sys_log_display_option* op , const char* msg ) {
	char tbuffer[512];
	char msgbuffer[1024];
	struct systime t;
	int sz;
	ti_grab_time(&t);
	sz= ti_print_time(&t,tbuffer,512);
	assert( sz >0 && sz <512 );
	sz = sprintf(msgbuffer,"<Time>:\n%s\n<Message>:\n%s\n",tbuffer,msg);
	assert( sz>0 && sz <1024 );

	fprintf((m->syslog->l)[op->idx],"%s",msgbuffer);
	if(op->flush)
		fflush((m->syslog->l)[op->idx]);
	if(op->err) {
		fprintf(op->err,"%s",msgbuffer);
	}
	if(op->out) {
		fprintf(op->out,"%s",msgbuffer);
	}
	if(op->op)
		op->op();
}

EXPORT_FUNC void master_write_log( struct master* m , const char* op , const char* fmt , ... ) {
	char buffer[1024];
	va_list l;
	int sz;
	va_start(l,fmt);
	sz = vsprintf(buffer,fmt,l);
	assert(sz>0 && sz<512 );
	_master_sys_log_do_display(m,_master_sys_log_translate_display_option(op),buffer);
}

/*
 * command and configuration internal API */

static struct master_cfg_file* _master_build_cfg( const char* fn ) {
	struct master_cfg_file* cfgf = malloc(sizeof(struct master_cfg_file));
	const char* c;
	cfgf->pars = kv_element_load_script(fn);
	/* Now loading the commands sequence into the list */
	cfgf->cmds = list_create( sizeof(const char*) , 10 );
	if( cfgf->pars == NULL )
		fprintf(stderr,"Configuration script loading error : %s.\nUsing default configuration parameter to initialize!",fn);
	/* Add as commands as execution order */
#define ADD_COMMAND(name) \
	c = name; \
	list_push_back(cfgf->cmds,(void*)(&c))


	ADD_COMMAND(_cmd_add_notification_slave_name());
	ADD_COMMAND(_cmd_load_slave_name());
	ADD_COMMAND(_cmd_add_thread_name());
	
#undef ADD_COMMAND

	return cfgf;
}

static void _master_exe_cfg_foreach( void* node,void* others ) {
	const char* c = *(const char**)node;
	struct master* m = (struct master*)others;
	struct kv_element* kv;
	struct master_command* cm;
	if( m->cfg_cmd->pars )
		kv = kv_element_query( m->cfg_cmd->pars , c );
	else
		kv = NULL;
	/* Here we should not care about the kv value, if the kv value is NULL , the corresponding
	 * command routine should be able to provide the default value for each command need to execute */
	cm = (struct master_command*)map_query(m->cmdmap,c,NULL);
	assert(cm);
	cm->cmd(m,kv);
}

static void _master_exe_cfg( struct master* m ) {
	list_foreach(m->cfg_cmd->cmds,_master_exe_cfg_foreach,m);
}

static void _master_reg_cmd( struct master* m , const char* cmd , struct master_command* mcd ) {
	struct map_kv kv;
	void* ret;
	kv.key = cmd;
	kv.val = mcd;
	ret = map_insert(m->cmdmap,&kv,NULL);
	assert(ret);
}


static void _master_join_thread( void* node,void* others ) {
	thread_join(*((thread_handler*)(node)), NULL , NULL );
}


/*
 * searching API for fetching specific resource internally owned by master */
struct slave* _master_find_slave( struct master* m , const char* slavename ) {
	struct slave** ret = map_query(m->slavemap,slavename,NULL);
	if(ret == NULL)
		return NULL;
	return *ret;
}


/* 
 * This function will internally destroy (clean) all the resource owned by the
 * master. Therefore , this function can only be invoked once the join operation
 * has been done properly , which means all the resource own by the thread has been
 * detached. 
 * In productive program ,the clean operation is not very useful when it comes to
 * free memory .
 * Additionally , this function will invoke all the clean callback once the slave
 * has been detached from the master
 */


static void _master_destroy_foreach( struct map_kv* mkv , void* args ) {
	struct slave* sl = (struct slave*)mkv->val;
	struct master* ms= (struct master*)(args);
	/* call the clean routine */
	_slave_clean_invoke(sl);
	/* destroy the related msg queue */
	msgqueue_destroy(_slave_ref_msgqueue(sl));
	/* unload the slave here */
	_slave_unload(sl);
}

static void _master_destroy( struct master* m ) {
	map_foreach(m->slavemap,_master_destroy_foreach,m);
	/* clean all the master related resource */
	map_destroy(m->slavemap);
	map_destroy(m->cmdmap);
	list_destroy(m->thpool);
	msgqueue_destroy(m->glb_msgqueue);
	if( m->cfg_cmd->pars )
		kv_element_destroy(m->cfg_cmd->pars);
	list_destroy(m->cfg_cmd->cmds);
	free(m->cfg_cmd);
}


EXPORT_FUNC void master_join( struct master* m ) {
	/* Join all the thread without notifying the exit command */
	list_foreach(m->thpool,_master_join_thread,NULL);
}

EXPORT_FUNC void master_run( struct master*m ) {
	/* Currently DO NOTHING */
}

EXPORT_FUNC struct master* master_create( const char* cfg ) {
	struct master* m;
	struct master_command cmd;
	m = malloc( sizeof(struct master) );
	/* create global message queue */
	m->glb_msgqueue = msgqueue_create(20,sizeof(struct slave*));
	/* create slave map */
	m->slavemap = map_create(sizeof(struct slave*),20,NULL);
	/* create thread pool */
	m->thpool = list_create(sizeof(dl_handler),12);
	/* create command map */
	m->cmdmap = map_create(sizeof(struct master_command),4,NULL);
	/* create system log */
	m->syslog = malloc( sizeof(struct master_sys_log) );
	if( !_master_sys_log_init( (m->syslog) ) ) {
		/* we will let the memory leak since the user should be able to
		 * create only one instance . And if a instance of master cannot
		 * be set up properly. There is no meaning that the program should
		 * continue to work. */
		return NULL;
	}
	/* initialize thread cmd */
	strcpy((char*)m->thread_cmd,"");
	/* register master configuration command */

#define MASTER_ADD_CMD(name,cb) \
	cmd.cmd = cb; \
	_master_reg_cmd(m,name,&cmd)

	MASTER_ADD_CMD(_cmd_add_thread_name(),_cmd_add_thread);
	MASTER_ADD_CMD(_cmd_load_slave_name(),_cmd_load_slave);
	MASTER_ADD_CMD(_cmd_add_notification_slave_name(),_cmd_add_notification_slave);

#undef MASTER_ADD_CMD
	/* building the configuration command structure */
	m->cfg_cmd = _master_build_cfg(cfg);
	/* configure the master and than starts to run */
	_master_exe_cfg(m);
	return m;
}


EXPORT_FUNC void master_destroy( struct master* m ) {

}





C_CODE_END




